<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { io, Socket  } from "socket.io-client"

// 房间号
const roomId = '001'

const caller = ref<boolean>(false) // 是否是发起方
const called = ref<boolean>(false) // 是否是接收方
const calling = ref<boolean>(false) // 呼叫中
const communicating = ref<boolean>(false) // 视频通话中
const localVideo = ref<HTMLVideoElement>() // video 标签实例，播放本人的视频
const remoteVideo = ref<HTMLVideoElement>() // video 标签实例，播放对方的视频
const socket = ref<Socket>()
const localStream = ref<any>() // 本地音视频流
const peer = ref<any>()

onMounted(() => {
  const sock = io('http://localhost:9999') // webrtc-server 启动的地址
  sock.on('connectionSuccess', () => {
    console.log('连接成功......')
    // 连接成功后，需要向服务器发起一个加入房间的事件；
    sock.emit('joinRoom',  roomId)
  })

  // 监听一下广播的视频请求事件
  sock.on('callRemote', (roomId) => {
    console.log("广播过来的视频请求的房间号：", roomId)
    // 如果是发送方自己发起的视频请求，这时候监听收到了，就不处理(自己发起的视频请求，被服务器广播后收到就不处理)
    // if (roomId === roomId) return
    // 或者说不是本用户A,发起的，才做处理
    if(!caller.value) {
      called.value = true // 接听方
      calling.value = true // 视频通话中
    }
  })

  // 监听信令服务器同意音视频事件(接收广播过来的消息)
  sock.on('acceptCall', async () => {
    console.log('信令服务器同意音视频事件', roomId)
    // 判断是否是接收方
    if(caller.value) { // 如果是我发出的请求，对方接收了音视频请求后，信令服务广播过来了，才需要处理
      // 发送方，即用户A收到用户B同意视频的请求
      // 开始交换 SDP 信息 和 candidate 信息
      // 1. 用户A创建 RTCPeerConnection， 添加本地音视频流，生成 offer, 并且通过信令服务器将offer发送给用户B
      // 创建RTCPeerConnection
      peer.value = new RTCPeerConnection()
      // 添加本地音视频流
      peer.value.addStream(localStream.value)

      // 5.发送方用户A获取candidate信息并通过信令服务器发送candidate信息给接收方用户B
      // 通过监听onicecandidate事件获取 candidate 信息
      peer.value.onicecandidate = (event: any) => {
        if (event.candidate) {
          console.log('用户A获取candidate信息', event.candidate)
          // 通过信令服务器发送 candidate 信息给用户B, 向信令服务器发送candidate信息
          socket.value?.emit('sendCandidate', { candidate: event.candidate, roomId })
        }
      }

      // 监听 onaddstream 来获取对方的音视频流
      peer.value.onaddstream = (event: any) => {
        console.log("用户A 收到 用户B的 stream 事件", event)
        // 把视频通话中的状态表示设置为true
        communicating.value = true
        // 把呼叫中的状态设置为false
        calling.value = false
        // 接收对方的stream,播放对方的视频
        remoteVideo.value!.srcObject = event.stream
        remoteVideo.value!.play()
      }

      // 生成 offer
      const offer = await peer.value.createOffer({
        offerToReceiveAudio: 1,
        offerToReceiveVideo: 1
      })

      console.log("offer", offer)

      // 设置本地描述，在本地设置信息
      await peer.value.setLocalDescription(offer)
      // 通过信令服务器将offer发送给用户B
      socket.value?.emit('sendOffer', { offer, roomId })
    }
  })

  // 2. 用户B收到用户A的offer
  // 收到offer
  sock.on('sendOffer', async (offer) => {
    // 因为用户A和用户B都在房间里，不是发送方收到的才处理，接收端(用户B)才处理
    if (called.value) {
      console.log(" 收到的offer ", offer)
      // 3. 用户B需要创建自己的RTCPeerConnection, 添加本地音视频流，设置远端描述信息，生成answer,并且通过信令服务器发送给用户A
      // 创建自己的RTCPeerConnection
      peer.value = new RTCPeerConnection()
      // 添加本地音视频流
      const stream = await getLocalStream()
      peer.value.addStream(stream)

      // 通过监听 onicecandidate 事件获取 candidate 信息
      peer.value.onicecandidate = (event: any) => {
        if (event.candidate) {
          console.log('用户B获取candidate信息', event.candidate)
          // 向服务器发送candidate信息， 通过信令服务器发送 candidate 信息给用户A
          socket.value?.emit('sendCandidate', { candidate: event.candidate, roomId })
        }
      }

      // 监听 onaddstream 来获取对方的音视频流
      peer.value.onaddstream = (event: any) => {
        console.log("用户B 收到 用户A的 stream 事件", event)
        // 把视频通话中的状态表示设置为true
        communicating.value = true
        // 把呼叫中的状态设置为false
        calling.value = false
        // 接收对方的stream,播放对方的视频
        remoteVideo.value!.srcObject = event.stream
        remoteVideo.value!.play()
      }

      // 设置远端描述信息,设置SDP信息
      await peer.value.setRemoteDescription(offer)
      // 生成answer
      const answer = await peer.value.createAnswer()
      console.log("接收方用户B生成的 answer::", answer)
      // 设置本地描述
      await peer.value.setLocalDescription(answer)
      // 通过信令服务器发送给用户A
      socket.value?.emit('sendAnswer', { answer, roomId })
    }
  })

  // 4.用户A收到 answer 信息
  sock.on('sendAnswer', async (answer) => {
    if (caller.value) { // 判断是否发送方
      console.log("用户A 收到的 answer::", answer)
      // 设置远端描述信息，设置SDP信息
      await peer.value?.setRemoteDescription(answer)
    }
  })

  // 收到candidate信息
  sock.on('sendCandidate', async (candidate) => {
      console.log(" 收到的 candidate::", candidate)
      // 添加 candidate 信息
      await peer.value?.addIceCandidate(candidate)
  })

  // 连接成功后，将 socket 实例赋值给 socket 变量
  socket.value = sock
})


// 获取本地音视频流
const getLocalStream = async () => {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true
  })
  localVideo.value!.srcObject = stream;
  localVideo.value!.play()
  // 保存一下本地音视频流
  localStream.value = stream
  return stream
}



// 发起方发起视频请求，例： 用户A 向 用户B 发起视频请求
const callRemote = async () => {
  console.log('发起视频')
  // 用户A 向 用户B 发起视频请求
  caller.value = true // 表示当前用户是发起方
  calling.value = true // 表示是否呼叫中
  await getLocalStream() // 发起视频的时候调用函数获取本地音视频流
  // 通过信令服务器向用户B发起视频请求
  socket.value?.emit('callRemote', roomId)
}

// 接收方同意视频请求
const acceptCall = () => {
  console.log('接收方同意视频请求')
  // 通过信令服务器通知用户A
  socket.value?.emit('acceptCall', roomId)
}

// 挂断视频
const hangUp = () => {
  console.log('挂断视频')
}
</script>
<template>
  <div class="flex items-center flex-col text-center p-12 h-screen">
    <div class="relative h-full mb-4">
      <video
          ref="localVideo"
          class="w-96 h-full bg-gray-200 mb-4 object-cover"
      ></video>
      <video
          ref="remoteVideo"
          class="w-32 h-48 absolute bottom-0 right-0 object-cover"
      ></video>
      <div v-if="caller && calling" class="absolute top-2/3 left-36 flex flex-col items-center">
        <p class="mb-4 text-white">等待对方接听......</p>
        <img src="/refuse.svg" class="w-16 cursor-pointer" alt="" @click="hangUp">
      </div>
      <div v-if="called && calling" class="absolute top-2/3 left-32 flex flex-col items-center">
        <p class="mb-4 text-white">收到视频邀请......</p>
        <div class="flex">
          <img @click="hangUp" src="/refuse.svg" alt="" class="w-16 cursor-pointer mr-4">
          <img @click="acceptCall" src="/accept.svg" alt="" class="w-16 cursor-pointer mr-4">
        </div>
      </div>
    </div>
    <div class="flex gap-2 mb-4">
      <button
          class="rounded-md bg-indigo-600 px-4 py-2 text-sm font-semibold text-white"
          @click="callRemote"
      >发起视频</button>
      <button
          class="rounded-md bg-red-600 px-4 py-2 text-sm font-semibold text-white"
          @click="hangUp"
      >挂断视频</button>
    </div>
  </div>
</template>
